export {};

// 类型推论: TypeScript里，在有些没有明确指出类型的地方，类型推论会帮助提供类型

let num = 1;
let arr = [1, 2, 3, 4, true, "123"];

// 高级类型
// 交叉类型 ： 交叉类型是将多个类型合并为一个类型     符号： &
// 联合类型 :  联合类型表示一个值可以是几种类型之一   符号： |
interface Person1 {
  name: string
}

interface Person2 {
  age: number
}

const p1: Person1 & Person2 = {
  name: '张三',
  age: 20
}

const p2: Person1 | Person2 = {
  age: 20
}

// 类型保护
interface O {
  fn?: Function
}


const o: O = {};

// 类型保护  if
if(o.fn) {
  o.fn()
}
// 类型保护  typeof 
if(typeof o.fn === 'function') {
  o.fn()
}

// 类型断言
document.addEventListener('click', e => {
  // e: 事件对象
  // e.target: 事件源
  const tar = e.target as HTMLElement

  if(tar.className === 'active') {
    console.log(123)
  }

  tar.classList.add('active')

  tar.remove()
})


// 类型别名： 
type abc = number

const n: abc = 1


// 像我们提到的，类型别名可以像接口一样；然而，仍有一些细微差别。
// 其一，接口创建了一个新的名字，可以在其它任何地方使用。 类型别名并不创建新名字—比如，错误信息就不会使用别名。 在下面的示例代码里，在编译器中将鼠标悬停在 interfaced上，显示它返回的是 Interface，但悬停在 aliased上时，显示的却是对象字面量类型。
// 另一个重要区别是类型别名不能被 extends和 implements（自己也不能 extends和 implements其它类型）。 因为 软件中的对象应该对于扩展是开放的，但是对于修改是封闭的，你应该尽量去使用接口代替类型别名。
// 另一方面，如果你无法通过接口来描述一个类型并且需要使用联合类型或元组类型，这时通常会使用类型别名。